﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Linq.Expressions;

namespace AMM.CustomWinControls {
    public partial class DataGridViewSelfDropDownEx : DropDownControlEx {

        #region Properties

        public int WidthEx { get; set; }
        public int HeightEx { get; set; }
        public int PageSizeEx { get; set; }
        public string DisplayMemberEx { get; set; }
        public string ValueMemberEx { get; set; }
        public object SelectedValueEx {
            get { return this._selectedValueEx; }
            set {
                if (value != null) {
                    object objInstance = this.DataGridViewSourceEx
                        .FirstOrDefault(x => x.GetType().GetProperty(this.ValueMemberEx).GetValue(x, null).Equals(value));
                    if (objInstance != null) {
                        this.SelectedItemEx = objInstance;
                        this.ValueEx = objInstance.GetType().GetProperty(this.DisplayMemberEx).GetValue(objInstance, null);
                    }
                    else {
                        this.SelectedItemEx = null;
                        this.ValueEx = null;
                    }
                    this.Invalidate();
                }
                this._selectedValueEx = value;
            }
        }
        public object SelectedItemEx { get; set; }
        public List<object> DataGridViewSourceEx { get; set; }

        #endregion

        public delegate void SearchHandler(object sender, SearchEventArgs e);
        public event SearchHandler Search;
        public event EventHandler ValueChanged;

        private int _pageIndex, _pageCount;
        private object _selectedValueEx;
        private List<object> _pageDataSource;
        private List<string> _hiddenColumnNameList;

        public override string Text {
            get { return this.ValueEx == null ? string.Empty : this.ValueEx.ToString(); }
            set { throw new Exception("Prohibiting set operation"); }
        }

        public DataGridViewSelfDropDownEx() {
            InitializeComponent();
            this.dgvList.AlternatingRowsDefaultCellStyle.BackColor = Color.LightGray;
            this.dgvList.IsShowRowHeaderNumberEx = true;

            this._hiddenColumnNameList = new List<string>();
        }

        public void DataBind<TViewModel, TViewModelProperties>(TViewModel viewModel,
            Expression<Func<TViewModel, TViewModelProperties>> viewModelAccessor) {
            if (viewModelAccessor.NodeType != ExpressionType.Lambda) {
                throw new ArgumentNullException("The viewModelAccessor is not a valid lambda expression.");
            }
            this.dgvList.DataGridViewModelType = typeof(TViewModel);

            LambdaExpression lambdaExpression = ((LambdaExpression)viewModelAccessor);
            if (lambdaExpression.Body.NodeType == ExpressionType.MemberAccess) {
                MemberExpression memberExpression = (MemberExpression)lambdaExpression.Body;
                this._hiddenColumnNameList.Add(memberExpression.Member.Name);
            }
            if (lambdaExpression.Body.NodeType == ExpressionType.New) {
                NewExpression newExpression = (NewExpression)lambdaExpression.Body;
                newExpression.Members.ToList().ForEach(x => { this._hiddenColumnNameList.Add(x.Name); });
            }

            this.dgvList.Columns.Cast<DataGridViewColumn>().ToList().ForEach(x => {
                x.Visible = this._hiddenColumnNameList.Contains(x.Name);
            });
        }

        protected void ResetDataSource() {
            this._pageIndex = 1;
            this._pageDataSource = this.DataGridViewSourceEx;
            this._pageCount = this._pageDataSource.Count / this.PageSizeEx + (this._pageDataSource.Count % this.PageSizeEx > 0 ? 1 : 0);

            this.tbSearchText.Text = string.Empty;
            this.lbTotalPages.Text = string.Format("/ {0}", this._pageCount);
            this.ReBindDataGridView();
            this.HiddenColumn();
        }

        protected void DataBind(Type viewModelType, List<string> showColumnNames) {
            this._hiddenColumnNameList = showColumnNames;
            this.dgvList.DataGridViewModelType = viewModelType;
            this.dgvList.Columns.Cast<DataGridViewColumn>().ToList().ForEach(x => {
                x.Visible = this._hiddenColumnNameList.Contains(x.Name);
            });
        }

        protected virtual void OnValueChanged(EventArgs eventargs) {
            if (this.ValueChanged != null) {
                this.ValueChanged(this, eventargs);
            }
        }

        private void OnSearch(object sender, SearchEventArgs e) {
            if (this.Search != null) {
                this.Search(sender, e);
            }
        }

        private void DataGridViewDropDownEx_Load(object sender, EventArgs e) {
            this.PageSizeEx = this.PageSizeEx == 0 ? 20 : this.PageSizeEx;
            this.HeightEx = this.HeightEx == 0 ? 121 : this.HeightEx;
            this.WidthEx = this.WidthEx < this.Width ? this.Width : this.WidthEx;
            this.pnlContainer.Width = (this.WidthEx - 2) < 221 ? 221 : this.WidthEx - 2;
            this.pnlContainer.Height = this.HeightEx;

            this._pageIndex = this._pageCount = 1;
            this._pageDataSource = this.DataGridViewSourceEx;
            if (this._pageDataSource != null) {
                this._pageCount = this._pageDataSource.Count / this.PageSizeEx + (this._pageDataSource.Count % this.PageSizeEx > 0 ? 1 : 0);
            }

            this.lbTotalPages.Text = string.Format("/ {0}", this._pageCount);
            this.ReBindDataGridView();
            this.HiddenColumn();

            this.DropDownShowed += new EventHandler(DataGridViewDropDownEx_DropDownShowed);
            this.InitializeDropDownEx(this.pnlContainer);
        }

        private void DataGridViewDropDownEx_DropDownShowed(object sender, EventArgs e) {
            if (this.SelectedItemEx != null) {
                int selectedIndex = this._pageDataSource.FindIndex(x => x == this.SelectedItemEx);
                this._pageIndex = ((selectedIndex + 1) / this.PageSizeEx) + ((selectedIndex + 1) % this.PageSizeEx > 0 ? 1 : 0);
                this._pageIndex = this._pageIndex <= 0 ? 1 : this._pageIndex;

                this.ReBindDataGridView();
                this.HiddenColumn();

                int rowIndex = selectedIndex - (this._pageIndex - 1) * this.PageSizeEx;
                if (rowIndex >= 0 && selectedIndex >= 0) {
                    this.dgvList.CurrentCell = this.dgvList
                        .Rows[rowIndex]
                        .Cells[this.DisplayMemberEx];
                }
            }
            else {
                this.ReBindDataGridView();
                this.HiddenColumn();
            }
        }

        private void ReBindDataGridView() {
            if (this._pageDataSource != null) {
                List<object> pageList = this._pageDataSource
                    .Skip((this._pageIndex - 1) * this.PageSizeEx)
                    .Take(this.PageSizeEx)
                    .ToList();
                this.dgvList.DataSource = new SortableBindingList<object>(pageList);
                this.dgvList.ClearSelection();
            }

            this.tbPage.Text = this._pageIndex.ToString();
            this.btnFirst.Enabled = this._pageIndex != 1;
            this.btnPrevious.Enabled = this._pageIndex > 1;
            this.btnNext.Enabled = this._pageIndex < this._pageCount;
            this.btnLast.Enabled = this._pageIndex != this._pageCount;
        }

        private void HiddenColumn() {
            this.dgvList.Columns.Cast<DataGridViewColumn>().ToList().ForEach(x => {
                x.Visible = this._hiddenColumnNameList.Contains(x.Name);
            });
            int i = 1;
            foreach (DataGridViewColumn column in this.dgvList.Columns) {
                if (column.Visible) {
                    column.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
                    if (i++ == this._hiddenColumnNameList.Count) {
                        column.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
                    }
                }
            }
        }

        private void pageButton_Click(object sender, EventArgs e) {
            Button btn = (Button)sender;
            switch (btn.Name) {
                case "btnFirst":
                    this._pageIndex = 1;
                    break;
                case "btnPrevious":
                    this._pageIndex -= 1;
                    break;
                case "btnNext":
                    this._pageIndex += 1;
                    break;
                case "btnLast":
                    this._pageIndex = this._pageCount;
                    break;
            }
            this.btnSearch_Click(null, EventArgs.Empty);
        }

        private void tbPage_KeyDown(object sender, KeyEventArgs e) {
            if (e.KeyCode == Keys.Enter) {
                int.TryParse(this.tbPage.Text, out this._pageIndex);
                this.ReBindDataGridView();
            }
        }

        private void btnSearch_Click(object sender, EventArgs e) {
            SearchEventArgs arg = new SearchEventArgs() {
                DataGridViewSourceEx = this.DataGridViewSourceEx,
                Value = this.tbSearchText.Text
            };
            this.OnSearch(sender, arg);
            this._pageDataSource = arg.DataGridViewSourceEx;
            this._pageCount = this._pageDataSource.Count / this.PageSizeEx + (this._pageDataSource.Count % this.PageSizeEx > 0 ? 1 : 0);
            this.lbTotalPages.Text = string.Format("/ {0}", this._pageCount);
            this.ReBindDataGridView();
            this.HiddenColumn();
        }

        private void btnReset_Click(object sender, EventArgs e) {
            this.SelectedValueEx = null;
            this.ValueEx = null;
            this.CloseDropDownEx();
        }

        private void dvList_CellClick(object sender, DataGridViewCellEventArgs e) {
            if (e.RowIndex >= 0) {
                this.SelectedItemEx = this.dgvList.Rows[e.RowIndex].DataBoundItem;
                object cellDisplayMember = this.dgvList.Rows[e.RowIndex].Cells[this.DisplayMemberEx].Value;
                object cellValueMember = this.dgvList.Rows[e.RowIndex].Cells[this.ValueMemberEx].Value;
                if (cellValueMember != null) {
                    this._selectedValueEx = cellValueMember.ToString();
                }
                if (cellDisplayMember != null) {
                    this.ValueEx = cellDisplayMember.ToString();
                }
                this.OnValueChanged(EventArgs.Empty);
                this.CloseDropDownEx();
            }
        }
    }

    public class SearchEventArgs : EventArgs {
        public List<object> DataGridViewSourceEx { get; set; }
        public string Value { get; set; }
    }
}
